package com.gin.no_gay_alliance.adpter.reply;

import static com.gin.nga.enums.AccountStatus.NOT_ACTIVATED;
import static com.gin.nga.enums.AccountStatus.NUKED;
import static com.gin.nga.enums.AccountStatus.UNREGISTERED;

import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.PopupMenu;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.RecyclerView;

import com.gin.common.utils.TimeUtils;
import com.gin.jackson.utils.ObjectUtils;
import com.gin.nga.enums.AccountStatus;
import com.gin.nga.enums.FromClient;
import com.gin.nga.enums.ReplyStatus;
import com.gin.nga.exception.NgaClientException;
import com.gin.nga.exception.NgaException;
import com.gin.nga.method.NukeApi;
import com.gin.nga.params.nuke.base.VoteParam;
import com.gin.nga.response.alterlog.LastEdit;
import com.gin.nga.response.alterlog.ReputationLog;
import com.gin.nga.response.body.BaseMessageBody;
import com.gin.nga.response.vote.BaseVoteData;
import com.gin.nga.response.vote.BetData;
import com.gin.nga.response.vote.BetOption;
import com.gin.nga.response.vote.VoteData;
import com.gin.nga.response.vote.VoteOption;
import com.gin.no_gay_alliance.R;
import com.gin.no_gay_alliance.activity.AttachmentListActivity;
import com.gin.no_gay_alliance.activity.UserCenterActivity;
import com.gin.no_gay_alliance.adpter.base.BaseBindingRecyclerViewAdapter;
import com.gin.no_gay_alliance.adpter.history.ReplyHistoryAdapter;
import com.gin.no_gay_alliance.adpter.medal.MedalAdapter;
import com.gin.no_gay_alliance.app.MyApplication;
import com.gin.no_gay_alliance.callback.Callback;
import com.gin.no_gay_alliance.callback.HandlerRequestCallback;
import com.gin.no_gay_alliance.customview.ContentWebViewRender;
import com.gin.no_gay_alliance.database.dao.NgaUserInfoDao;
import com.gin.no_gay_alliance.database.entity.NgaUserInfo;
import com.gin.no_gay_alliance.databinding.ItemBetOptionBinding;
import com.gin.no_gay_alliance.databinding.ItemReplyBinding;
import com.gin.no_gay_alliance.databinding.ItemVoteOptionBinding;
import com.gin.no_gay_alliance.enums.MessageWhat;
import com.gin.no_gay_alliance.listener.AgreeOnclickListener;
import com.gin.no_gay_alliance.listener.ReplyItemPopupOnMenuItemClickListener;
import com.gin.no_gay_alliance.param.PreparePostParam;
import com.gin.no_gay_alliance.utils.AlertDialogUtils;
import com.gin.no_gay_alliance.utils.ImageUtils;
import com.gin.no_gay_alliance.utils.InputMethodManagerUtils;
import com.gin.no_gay_alliance.utils.ReplyStatusUtils;
import com.gin.no_gay_alliance.utils.ViewUtils;
import com.gin.no_gay_alliance.viewmodel.ReplyItem;

import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;

import okhttp3.Call;

/**
 * @author : ginstone
 * @version : v1.0.0
 * @since : 2023/8/24 16:48
 **/
public class ReplyItemAdapter extends BaseBindingRecyclerViewAdapter<ItemReplyBinding, ReplyItem> implements View.OnClickListener, Callback<BaseMessageBody> {
    public static final String TAG = ReplyItemAdapter.class.getSimpleName();
    /**
     * 需要显示的状态
     */
    public static final List<AccountStatus> SHOW_STATUS = new ArrayList<>();

    static {
        SHOW_STATUS.add(NOT_ACTIVATED);
        SHOW_STATUS.add(NUKED);
        SHOW_STATUS.add(UNREGISTERED);
    }

    private final ReplyItemPopupOnMenuItemClickListener popupMenuItemOnClickListener;
    /**
     * 选中的投票选项id
     */
    private final Set<Integer> checkedVoteOptionId = new HashSet<>();
    ContentWebViewRender contentWebViewRender;

    public ReplyItemAdapter(@NonNull FragmentActivity activity, @NonNull List<ReplyItem> data) {
        super(activity, data);
        popupMenuItemOnClickListener = new ReplyItemPopupOnMenuItemClickListener(activity);
        contentWebViewRender = new ContentWebViewRender(activity, handler);
    }

    @Override
    protected void onBindViewHolder(ViewHolder<ItemReplyBinding> holder, @NonNull ItemReplyBinding b, int position, ReplyItem item) {
        // 历史版本
        ViewUtils.setGone(ObjectUtils.isEmpty(item.versions), b.ivVersions);
        // 投票数据
        handleVoteData(b, item.voteData, item.topicId);

        // 渲染正文
        contentWebViewRender.render(position, b.layoutContent, holder, item.nodes);
        // 如果是被处罚的回复，添加背景色
        if (item.statuses != null && item.statuses.contains(ReplyStatus.HIDE_TITLE)) {
            Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.shape_less_nuke, context.getTheme());
            b.layoutContent.setBackground(drawable);
        } else {
            b.layoutContent.setBackground(null);
        }
        // 小尾巴
        if (item.avatarBuff != null) {
            String suffix = item.avatarBuff.getSuffix();
            if (!ObjectUtils.isEmpty(suffix)) {
                b.tvTail.setText(String.format("%s~", suffix));
                b.tvTail.setVisibility(View.VISIBLE);
            } else {
                b.tvTail.setVisibility(View.GONE);
            }
        } else {
            b.tvTail.setVisibility(View.GONE);
        }

        // 回复状态
        ReplyStatusUtils.configReply(item.statuses, b.layoutTopicStatus, context);
        // 礼物
        ViewUtils.setGone(ObjectUtils.isEmpty(item.gifts), b.rvGift);
        b.rvGift.setAdapter(new GiftAdapter(context, item.gifts));

        // 附件信息
        ViewUtils.setGone(ObjectUtils.isEmpty(item.attachments), b.ivAttachment);

        //用户名
        b.tvUsername.setText(item.username);
        // 匿名用户处理
        ViewUtils.setGone(!item.anonymous, b.tvAnonymous);

        //威望
        if (item.group != null && item.prestige != null) {
            String text = String.format(Locale.CHINA, "%s: %s", item.group.getName(), item.prestige / 10.0);
            b.tvPrestige.setText(text);
            b.tvPrestige.setVisibility(View.VISIBLE);
        } else {
            b.tvPrestige.setVisibility(View.GONE);
        }
        //发帖数
        setVisibleOrGone(item.postCount != null && item.postCount > 0, b.tvPostCount);
        b.tvPostCount.setText(String.format(Locale.CHINA, "发帖 %d", item.postCount));
        // 客户端
        String stringClient = toStringClient(item.fromClient);
        b.tvClient.setText(stringClient);
        setVisibleOrGone(stringClient != null, b.tvClient);
        // 楼层号
        b.tvFloorNumber.setText(String.format(Locale.CHINA, "%d 楼", item.floorNumber));
        ViewUtils.setGone(item.floorNumber == null, b.tvFloorNumber);
        // 标题
        b.tvTitle.setText(item.title);
        ViewUtils.setGone(ObjectUtils.isEmpty(item.title), b.tvTitle);
        // 发布时间
        b.tvPostTime.setText(item.postDatetime != null ? TimeUtils.format(item.postDatetime, TimeUtils.SHORT_FORMATTER_2) : null);
        ViewUtils.setGone(item.postDatetime == null, b.tvPostTime);
        //最后修改
        b.tvLastEdit.setText(toStringLastEdit(item.lastEdit));
        ViewUtils.setGone(item.lastEdit == null, b.tvLastEdit);
        // 赞数
        b.tvAgreeCount.setText(String.valueOf(item.agreeCount));

        // 声望
        ViewUtils.setGone(item.reputation == null, b.tvReputation);
        if (item.reputation != null) {
            String text = String.format(Locale.CHINA, "%s (%s)", item.reputation.getLabel(), item.reputation.getValue());
            b.tvReputation.setText(text);
        }
        // 徽章
        ViewUtils.setGone(ObjectUtils.isEmpty(item.medals), b.tvMedal);
        b.rvMedal.setAdapter(new MedalAdapter(context, !ObjectUtils.isEmpty(item.medals) ? item.medals : new ArrayList<>()));

        // 如果用户id不存在(不可见楼层)
        View[] hideViewsWhenNoAuthor = {b.ivUserAvatar, b.ivAgree, b.ivDisagree, b.tvAgreeCount, b.ivMore, b.ivReply, b.ivQuote, b.ivShare, b.ivUser, b.tvAccountStatus, b.tvIpLocation};
        if (item.authorUid == null) {
            // 隐藏无效按钮
            ViewUtils.setGone(true, hideViewsWhenNoAuthor);
        } else {
            // 显示按钮
            ViewUtils.setGone(false, hideViewsWhenNoAuthor);

            //匿名用户
            if (item.anonymous) {
                ViewUtils.setInvisible(true, b.ivUser);
                ViewUtils.setGone(true, b.tvAccountStatus, b.tvIpLocation);
                b.ivUserAvatar.setImageResource(R.drawable.ic_anonymous_avatar);
            } else {
                ImageUtils.loadUserAvatar(context, item.avatar, b.ivUserAvatar);
                // 账号状态
                NgaUserInfoDao ngaUserInfoDao = MyApplication.app.ngaDatabase.ngaUserInfoDao();
                NgaUserInfo ngaUserInfo = ngaUserInfoDao.get(item.authorUid);
                if (ngaUserInfo != null) {
                    // 账号状态
                    b.tvAccountStatus.setText(ngaUserInfo.accountStatus.label);
                    setVisibleOrGone(SHOW_STATUS.contains(ngaUserInfo.accountStatus), b.tvAccountStatus);
                    // ip
                    b.tvIpLocation.setText(ngaUserInfo.ipLocation);
                    setVisibleOrGone(ngaUserInfo.ipLocation != null, b.tvIpLocation);
                }
            }
        }

        //  评论
        configHotReplyComment(item.comment, b.tvComment, b.dividerComment, b.rvComment, item.showComment);
        b.tvComment.setText(String.format("评论 %s", item.showComment ? "-" : "+"));
        b.tvComment.setOnClickListener(v -> {
            item.showComment = !item.showComment;
            b.tvComment.setText(String.format("评论 %s", item.showComment ? "-" : "+"));
            ViewUtils.setGone(!item.showComment, b.rvComment);
        });
        //  热门回复
        configHotReplyComment(item.hotReplies, b.tvHotReply, b.dividerHotReply, b.rvHotReply, item.showHotReplies);
        b.tvHotReply.setText(String.format("热门回复 %s", item.showHotReplies ? "-" : "+"));
        b.tvHotReply.setOnClickListener(v -> {
            item.showHotReplies = !item.showHotReplies;
            b.tvHotReply.setText(String.format("热门回复 %s", item.showHotReplies ? "-" : "+"));
            ViewUtils.setGone(!item.showHotReplies, b.rvHotReply);
        });

        // 操作日志
        List<ReputationLog> logs = item.logs;
        // 控制是否显示
        ViewUtils.setGone(ObjectUtils.isEmpty(logs), b.tvOperationLog, b.dividerOperationLog, b.rvOperationLog);
        if (!ObjectUtils.isEmpty(logs)) {
            b.rvOperationLog.setAdapter(new OperationLogAdapter(context, logs));
        }

        // 点击事件
        View[] clickViews = {b.ivMore, b.ivShare, b.ivUser, b.ivReply, b.ivQuote, b.ivUserAvatar, b.tvUsername, b.ivAttachment, b.ivVersions};
        setTag(item, clickViews);
        ViewUtils.setOnClickListener(this, clickViews);
        // 赞踩点击事件
        ViewUtils.setOnClickListener(new AgreeOnclickListener(handler, b.tvAgreeCount, item), b.ivAgree, b.ivDisagree);
    }

    @NonNull
    @Override
    protected ItemReplyBinding buildBinding(LayoutInflater inflater) {
        return ItemReplyBinding.inflate(inflater);
    }

    @Override
    public void onClick(View v) {
        // 回复底部按钮点击
        if (v.getTag() instanceof ReplyItem) {
            ReplyItem item = (ReplyItem) v.getTag();
            int id = v.getId();
            // 历史版本
            if (v.getId() == R.id.iv_versions) {
                ReplyHistoryAdapter.showReplyHistoryDialog(context, item.versions);
            }
            // 附件信息
            if (id == R.id.iv_attachment) {
                AttachmentListActivity.startActivity(context, item.attachments);
            }

            // 打开用户中心
            if (id == R.id.tv_username || id == R.id.iv_user_avatar) {
                UserCenterActivity.startActivity(context, item.authorUid);
            }
            // 回复
            if (id == R.id.iv_reply) {
                PreparePostParam p = new PreparePostParam(PreparePostParam.Action.REPLY, item.topicId, item.replyId);
                handler.sendMessage(MessageWhat.START_ACTIVITY_POST_ACTIVITY, p);
            }
            // 引用
            if (id == R.id.iv_quote) {
                PreparePostParam p = new PreparePostParam(PreparePostParam.Action.QUOTE, item.topicId, item.replyId);
                handler.sendMessage(MessageWhat.START_ACTIVITY_POST_ACTIVITY, p);
            }
            // 激活弹窗的按钮
            if (id == R.id.iv_more || id == R.id.iv_share || id == R.id.iv_user) {
                PopupMenu popupMenu = new PopupMenu(context, v);
                // 更多按钮
                if (id == R.id.iv_more) {
                    popupMenu.inflate(R.menu.reply_menu);
                    // 当前回复是否已关闭提示
                    boolean noHint = item.statuses != null && item.statuses.contains(ReplyStatus.NO_HINT);
                    // 配置提醒按钮的显示情况
                    popupMenu.getMenu().findItem(R.id.tv_notify_off).setVisible(!noHint);
                    popupMenu.getMenu().findItem(R.id.tv_notify_on).setVisible(noHint);
                }
                if (id == R.id.iv_share) {
                    popupMenu.inflate(R.menu.share_menu);
                }
                if (id == R.id.iv_user) {
                    popupMenu.inflate(R.menu.reply_user_menu);
                }
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    popupMenu.setForceShowIcon(true);
                }
                popupMenuItemOnClickListener.setItem(item);
                popupMenu.setOnMenuItemClickListener(popupMenuItemOnClickListener);
                popupMenu.show();
            }
        }

    }

    /**
     * 请求成功回调
     *
     * @param data 数据
     */
    @Override
    public void onSuccess(BaseMessageBody data) {
        handler.toast(data.getMessage());
    }

    /**
     * 更新投票按钮的可用状态
     *
     * @param b            binding
     * @param baseVoteData 投票数据
     */
    private void checkVoteButtonEnable(@NonNull ItemReplyBinding b, BaseVoteData baseVoteData) {
        int checkCount = checkedVoteOptionId.size();
        Integer maxSelect = baseVoteData.getMaxSelect();
        if (baseVoteData.getEnd() != null && baseVoteData.getEnd().isBefore(ZonedDateTime.now())) {
            b.btVote.setText("投票时限已过");
            b.btVote.setEnabled(false);
        } else if (checkCount == 0) {
            b.btVote.setText("至少选择一项");
            b.btVote.setEnabled(false);
        } else if (maxSelect != null) {
            if (checkCount > maxSelect) {
                b.btVote.setEnabled(false);
                b.btVote.setText(String.format(Locale.CHINA, "最多选择 %d 项, 目前选中 %d 项", maxSelect, checkCount));
            } else {
                b.btVote.setEnabled(true);
                b.btVote.setText(R.string.vote);
            }
        } else {
            b.btVote.setEnabled(true);
        }
    }

    /**
     * 配置评论和热门回复
     *
     * @param data    数据
     * @param tvTitle 标题
     * @param divider 分割线
     * @param rv      RecyclerView
     * @param show
     */
    private void configHotReplyComment(List<ReplyItem> data, TextView tvTitle, View divider, RecyclerView rv, boolean show) {
        ViewUtils.setGone(ObjectUtils.isEmpty(data), tvTitle, divider);
        ViewUtils.setGone(ObjectUtils.isEmpty(data) || !show, rv);
        ReplyItemAdapter adapter = new ReplyItemAdapter((FragmentActivity) context, data);
        rv.setAdapter(adapter);
        adapter.setBlockNetworkImage(false);
    }

    /**
     * 处理投票和投注
     *
     * @param b            binding
     * @param baseVoteData 投票或投注数据
     * @param topicId      主题id
     */
    private void handleVoteData(@NonNull ItemReplyBinding b, BaseVoteData baseVoteData, Long topicId) {
        // 投票数据显示状态
        ViewUtils.setGone(hideVote(baseVoteData), b.rvVote, b.tvVoteTips, b.tvVoteTitle);
        // 投票按钮显示状态
        ViewUtils.setGone(!(baseVoteData instanceof VoteData), b.btVote);
        if (baseVoteData == null) {
            return;
        }
        // 提示
        StringBuilder tipsBuilder = new StringBuilder();
        tipsBuilder.append("要求: ");
        if (baseVoteData.getReputation() != null) {
            tipsBuilder.append("声望大于 ").append(baseVoteData.getReputation()).append(" ");
        }
        if (baseVoteData.getMaxSelect() != null) {
            tipsBuilder.append("最多选择 ").append(baseVoteData.getMaxSelect()).append(" 项");
        }
        if (baseVoteData.getResult() != null) {
            switch (baseVoteData.getResult()) {
                case AFTER_VOTE:
                    tipsBuilder.append(" 投票后查看结果 ");
                    break;
                case AFTER_END:
                    tipsBuilder.append(" 结束后查看结果 ");
                    break;
            }
        }
        tipsBuilder.append("\n");

        if (baseVoteData instanceof BetData) {
            BetData betData = (BetData) baseVoteData;
            //投注
            b.tvVoteTitle.setText("投注");
            // 提示
            StringBuilder sb = new StringBuilder("点击项目投注铜币");
            Integer min = betData.getMin();
            Integer max = betData.getMax();
            if (min != null) {
                sb.append(" 最少").append(min);
            }
            if (max != null) {
                sb.append(" 最多").append(max);
            }
            sb.append("\n");
            tipsBuilder.insert(0, sb);
            tipsBuilder.append(String.format(Locale.CHINA, "总参与: %d 人", baseVoteData.getTotalCount()));
            tipsBuilder.append(String.format(Locale.CHINA, " 总投注: %d 铜", betData.getTotalMoney()));
            tipsBuilder.append("\n");

            // 投注选项 & 投注请求
            b.rvVote.setAdapter(new BaseBindingRecyclerViewAdapter<ItemBetOptionBinding, BetOption>(context, betData.getOptions()) {
                @Override
                protected void onBindViewHolder(ViewHolder<ItemBetOptionBinding> holder, @NonNull ItemBetOptionBinding b, int position, BetOption option) {
                    // 进度占比
                    b.progressBar.setMax(Math.toIntExact(betData.getTotalMoney()));
                    b.progressBar.setProgress(Math.toIntExact(option.getMoney()), true);
                    b.tvMoney.setText(String.format(Locale.CHINA, "%s %d 铜(%.1f%%)", option.getLabel(), option.getMoney(), option.getMoneyPer() * 100));
                    b.tvPerson.setText(String.format(Locale.CHINA, "%d 人(%.1f%%)", option.getCount(), option.getCountPer() * 100));
                    ViewUtils.setGone(!Objects.equals(betData.getSuccessId(), option.getId()), b.tvTagWin);
                    //点击事件

                    b.getRoot().setOnClickListener(v -> {
                        if (baseVoteData.getEnd() != null && baseVoteData.getEnd().isBefore(ZonedDateTime.now())) {
                            handler.toast("投注结束时间已过");
                        } else {
                            EditText et = new EditText(context);
                            et.setInputType(EditorInfo.TYPE_CLASS_NUMBER);
                            // 初始值
                            if (min != null) {
                                et.setText(String.valueOf(min));
                                et.setSelection(0, et.getText().length());
                            }

                            String message = String.format(Locale.CHINA, "请输入投注金额: %d ~ %d", min != null ? min : 0, max);
                            AlertDialog dialog = AlertDialogUtils.showConfirm(context, "投注: " + option.getLabel(), message, et, (d, which) -> {
                                if (which == DialogInterface.BUTTON_POSITIVE) {
                                    // 发送请求
                                    VoteParam param = new VoteParam(topicId, option.getId(), Integer.parseInt(String.valueOf(et.getText())));
                                    NukeApi.vote(MyApplication.getClient(), param).async(new HandlerRequestCallback<>(ReplyItemAdapter.this, handler));
                                }
                            });

                            // 确定按钮
                            Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);

                            et.requestFocus();
                            InputMethodManagerUtils.show(handler, et);

                            // 监控输入
                            et.addTextChangedListener(new TextWatcher() {
                                @Override
                                public void beforeTextChanged(CharSequence s, int start, int count, int after) {

                                }

                                @Override
                                public void onTextChanged(CharSequence s, int start, int before, int count) {
                                    if (ObjectUtils.isEmpty(s)) {
                                        positiveButton.setEnabled(false);
                                    } else {
                                        int value = Integer.parseInt(String.valueOf(s));
                                        if (min != null && value < min) {
                                            // 禁用确定按钮
                                            positiveButton.setEnabled(false);
                                        } else if (max != null && value > max) {
                                            // 替换原内容
                                            et.setText(String.valueOf(max));
                                            et.setSelection(0, et.getText().length());
                                            positiveButton.setEnabled(true);
                                        } else {
                                            // 启用确定按钮
                                            positiveButton.setEnabled(true);
                                        }
                                    }
                                }

                                @Override
                                public void afterTextChanged(Editable s) {

                                }
                            });
                        }

                    });
                }
            });

        } else if (baseVoteData instanceof VoteData) {
            VoteData voteData = (VoteData) baseVoteData;
            // 投票
            b.tvVoteTitle.setText("投票");
            // 投票按钮状态
            checkVoteButtonEnable(b, baseVoteData);
            // 提示
            tipsBuilder.append(String.format(Locale.CHINA, "总参与: %d 人", baseVoteData.getTotal()));
            tipsBuilder.append(String.format(Locale.CHINA, " 总票数: %d 票", baseVoteData.getTotalCount()));
            tipsBuilder.append("\n");


            //  投票选项
            b.rvVote.setAdapter(new BaseBindingRecyclerViewAdapter<ItemVoteOptionBinding, VoteOption>(context, voteData.getOptions()) {
                @Override
                protected void onBindViewHolder(ViewHolder<ItemVoteOptionBinding> holder, @NonNull ItemVoteOptionBinding bb, int position, VoteOption option) {
                    // 如果没数据，隐藏进度条和占比文字
                    ViewUtils.setGone(baseVoteData.getTotalCount() == 0, bb.progressBar, bb.tvCount);
                    // 进度占比
                    bb.progressBar.setMax(baseVoteData.getTotalCount());
                    bb.progressBar.setProgress(option.getCount(), true);
                    // 占比
                    bb.tvCount.setText(String.format(Locale.CHINA, "%d 票(%.1f%%)", option.getCount(), option.getCountPer() * 100));
                    // 标题
                    bb.checkBox.setText(option.getLabel());
                    // 是否选中
                    bb.checkBox.setChecked(checkedVoteOptionId.contains(option.getId()));
                    // 切换选中状态
                    bb.checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
                        if (isChecked) {
                            checkedVoteOptionId.add(option.getId());
                        } else {
                            checkedVoteOptionId.remove(option.getId());
                        }
                        checkVoteButtonEnable(b, baseVoteData);
                    });
                    // 点击所有位置都可以切换选中状态
                    bb.getRoot().setOnClickListener(v -> bb.checkBox.setChecked(!bb.checkBox.isChecked()));
                }
            });
            // 执行投票请求
            b.btVote.setOnClickListener(v -> {

                // 发送请求
                VoteParam param = new VoteParam(topicId, new ArrayList<>(checkedVoteOptionId));
                NukeApi.vote(MyApplication.getClient(), param).async(new HandlerRequestCallback<>(new Callback<BaseMessageBody>() {
                    @Override
                    public void onSuccess(BaseMessageBody data) {
                        handler.toast(data.getMessage());
                        handler.post(() -> {
                            b.btVote.setText("已投票");
                            b.btVote.setEnabled(false);
                        });
                    }

                    @Override
                    public void beforeRequest() {
                        b.btVote.setText("投票中..");
                        b.btVote.setEnabled(false);
                    }

                    @Override
                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                        Callback.super.onFailure(call, e);
                        handler.post(() -> {
                            b.btVote.setText("投票");
                            b.btVote.setEnabled(true);
                        });
                    }

                    @Override
                    public void handleException(NgaException e) {
                        handler.post(() -> {
                            b.btVote.setText("投票");
                            b.btVote.setEnabled(true);
                        });
                        if (e instanceof NgaClientException) {
                            String s = ((NgaClientException) e).getReason().get(0);
                            if (s != null && s.contains("投过")) {
                                handler.post(() -> {
                                    b.btVote.setText("已投票");
                                    b.btVote.setEnabled(false);
                                });
                            }
                        } else {
                            Callback.super.handleException(e);
                        }
                    }
                }, handler));
            });
        }

        // 提示信息
        if (baseVoteData.getEnd() != null) {
            tipsBuilder.append("结束时间: ").append(TimeUtils.format(baseVoteData.getEnd(), TimeUtils.SHORT_FORMATTER));
        }
        b.tvVoteTips.setText(tipsBuilder.toString());
    }

    public static String toStringClient(FromClient client) {
        if (client == null || client == FromClient.UNKNOWN) {
            return null;
        }
        return client.name();
    }

    public static String toStringLastEdit(LastEdit lastEdit) {
        if (lastEdit == null) {
            return null;
        }
        String time = TimeUtils.format(lastEdit.getTimestamp(), TimeUtils.SHORT_FORMATTER);
        String username = lastEdit.getUsername();
        if (!ObjectUtils.isEmpty(username)) {
            return String.format(Locale.CHINA, "由 %s 修改: %s", username, time);
        }
        return "修改: " + time;
    }

    /**
     * 是否隐藏投票控件
     *
     * @param baseVoteData 投票数据
     * @return
     */
    private static boolean hideVote(BaseVoteData baseVoteData) {
        if (baseVoteData == null) {
            return true;
        }
        if (baseVoteData instanceof VoteData && ObjectUtils.isEmpty(((VoteData) baseVoteData).getOptions())) {
            return true;
        }
        return baseVoteData instanceof BetData && ObjectUtils.isEmpty(((BetData) baseVoteData).getOptions());
    }

    /**
     * 是否阻止WebView加载图片
     *
     * @param blocked 是否阻止加载图片
     */
    public void setBlockNetworkImage(boolean blocked) {
        handler.postDelayed(() -> contentWebViewRender.setBlockNetworkImage(blocked), 50);
    }

    /**
     * 设置当前版面id，供操作按钮使用
     *
     * @param forumId 版面id
     */
    public void setForumId(long forumId) {
        popupMenuItemOnClickListener.setForumId(forumId);
    }
}
